#include <cstdlib>
#include <cstring>
#include <iostream>
#include "string.h"

using std::cerr;
using std::endl;

class EMalloc{};
class ERalloc{};

constexpr unsigned roundUp(unsigned n, unsigned unit){
	return (n + unit - 1) / unit * unit;
}

String::String(const char *str)
	: String()
{
	m_size = strlen(str);
	m_spaceSize = roundUp(m_size + 1, INCREMENT);
	try{
		m_base = (char*)malloc(m_spaceSize);
		if(m_base == nullptr)
			throw(EMalloc());
		strcpy(m_base, str);
	}
	catch(EMalloc){
		cerr << "fail to malloc" << endl;
		exit(-1);
	}
}

String::String(char c, unsigned n){
	m_size = n;
	m_spaceSize = roundUp(n + 1, INCREMENT);
	m_base = (char*)malloc(m_spaceSize);
	if(m_base == nullptr)
		cerr << "fail to malloc in String()\n";
	for(int i = 0; i < n; i++){
		m_base[i] = c;
	}
	m_base[n] = '\0';
}

String::String(const String &s){
	m_size = s.m_size;
	m_spaceSize = s.m_spaceSize;
	m_base = (char*)malloc(m_spaceSize);
	if(m_base == nullptr)
		cerr << "fail to malloc in String(const String&)\n";
	strcpy(m_base, s.m_base);
}

String::~String(){
	free(m_base);
}

String& String::operator=(const String &s){
	if(m_spaceSize < s.m_size + 1){
		m_base = (char*)realloc(m_base, s.m_spaceSize);
		if(m_base == nullptr)
			cerr << "realloc failed in String::operator=()\n";
		m_spaceSize = s.m_spaceSize;
	}
	m_size = s.m_size;
	strcpy(m_base, s.m_base);
}

unsigned String::size() const{
	return m_size;
}

String String::operator+(const String &s) const{
	String retStr(*this);
	if(retStr.m_size + s.m_size + 1 > retStr.m_spaceSize){
		retStr.m_spaceSize = roundUp(retStr.m_size + s.m_size + 1, INCREMENT);
		retStr.m_base = (char*)realloc(retStr.m_base, retStr.m_spaceSize);
		if(retStr.m_base == nullptr)
			cerr << "realloc fail in String::operator+()\n";
	}
	unsigned oldsize = retStr.m_size;
	retStr.m_size += s.m_size;
	for(int i = 0; i < s.m_size; i++){
		retStr[oldsize + i] = s[i];
	}
	return retStr;
}

String& String::operator+=(const String &s){
	if(m_size + s.m_size + 1 > m_spaceSize){
		m_spaceSize = roundUp(m_size + s.m_size + 1, INCREMENT);
		m_base = (char*)realloc(m_base, m_spaceSize);
		if(m_base == nullptr)
			cerr << "realloc failed in String::operator+=()\n";
	}
	unsigned oldsize = m_size;
	m_size += s.m_size;
	for(int i = 0; i < s.m_size; i++){
		m_base[oldsize + i] = s[i];
	}
	return *this;
}

char& String::operator[](unsigned index) const{
	if(index > m_size){
		cerr << "out of range in String::operator[]\n";
		exit(1);
	}
	return m_base[index];
}

char* String::toStr() const{
	return m_base;
}

std::ostream& operator<<(std::ostream &os, const String &s){
	for(int i = 0; i < s.size(); i++){
		os << s[i];
	}
	return os;
}
